﻿Imports System.Windows
Imports System.Windows.Input
Imports System.Collections.ObjectModel
Imports System.Windows.Markup
Imports System.Windows.Data

Public NotInheritable Class EventToCommand
    Inherits DependencyObject


    Public Shared Function GetEvent(ByVal element As DependencyObject) As RoutedEvent
        If element Is Nothing Then
            Throw New ArgumentNullException("element")
        End If

        Return element.GetValue(EventProperty)
    End Function
    Public Shared Sub SetEvent(ByVal element As DependencyObject, ByVal value As RoutedEvent)
        If element Is Nothing Then
            Throw New ArgumentNullException("element")
        End If

        element.SetValue(EventProperty, value)
    End Sub

    Public Shared ReadOnly EventProperty As  _
                           DependencyProperty = DependencyProperty.RegisterAttached("Event", _
                           GetType(RoutedEvent), GetType(EventToCommand), _
                           New UIPropertyMetadata(AddressOf EventChanged))

    Public Shared Function GetCommand(ByVal element As DependencyObject) As ICommand
        If element Is Nothing Then
            Throw New ArgumentNullException("element")
        End If

        Return element.GetValue(CommandProperty)
    End Function
    Public Shared Sub SetCommand(ByVal element As DependencyObject, ByVal value As ICommand)
        If element Is Nothing Then
            Throw New ArgumentNullException("element")
        End If

        element.SetValue(CommandProperty, value)
    End Sub

    Public Shared ReadOnly CommandProperty As  _
                           DependencyProperty = DependencyProperty.RegisterAttached("Command", _
                           GetType(ICommand), GetType(EventToCommand), _
                           New UIPropertyMetadata(AddressOf CommandChanged))

    Private Shared Sub CommandChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
        If d Is Nothing Then Return
        Dim uid As UIElement = TryCast(d, UIElement)
        If uid Is Nothing Then Return
        If Not data.ContainsKey(uid) Then
            data.Add(uid, New Token())
        End If
        data(uid).Command = e.NewValue

    End Sub
    Private Shared Sub EventChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
        If d Is Nothing Then Return
        Dim uid As UIElement = TryCast(d, UIElement)
        If uid Is Nothing Then Return
        If Not data.ContainsKey(uid) Then
            data.Add(uid, New Token())
        End If
        data(uid).Event = e.NewValue
        If e.NewValue Is Nothing Then
            uid.RemoveHandler(e.OldValue, data(uid).handler)
        Else
            data(uid).handler = New RoutedEventHandler(AddressOf Handler)
            uid.AddHandler(data(uid).Event, data(uid).handler)
        End If

    End Sub
    Private Shared Sub Handler(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs)
        Dim uid As UIElement = TryCast(sender, UIElement)
        If uid Is Nothing Then Return
        If data(uid).Command Is Nothing Then Return
        If data(uid).Command.CanExecute(e) Then
            data(uid).Command.Execute(e)
        End If
    End Sub

    Private Shared data As New Dictionary(Of UIElement, Token)

    Private Class Token
        Friend handler As RoutedEventHandler
        Private ev As RoutedEvent
        Private cm As ICommand

        Sub New()

        End Sub
        Sub New(ByVal ev As RoutedEvent)
            Me.ev = ev
        End Sub
        Sub New(ByVal cm As ICommand)
            Me.cm = cm
        End Sub
        Sub New(ByVal ev As RoutedEvent, ByVal cm As ICommand)
            Me.ev = ev
            Me.cm = cm
        End Sub

        Public Property [Event] As RoutedEvent
            Get
                Return ev
            End Get
            Set(ByVal value As RoutedEvent)
                ev = value
            End Set
        End Property
        Public Property [Command] As ICommand
            Get
                Return cm
            End Get
            Set(ByVal value As ICommand)
                cm = value
            End Set
        End Property
    End Class
End Class

<ContentProperty("Items")> _
Public NotInheritable Class EventToCommandAggregator
    Inherits DependencyObject



    Public Shared Function GetItems(ByVal element As DependencyObject) As IEnumerable(Of EventToCommandItem)
        If element Is Nothing Then
            Throw New ArgumentNullException("element")
        End If

        Return element.GetValue(ItemsProperty)
    End Function

    Public Shared Sub SetItems(ByVal element As DependencyObject, ByVal value As IEnumerable(Of EventToCommandItem))
        If element Is Nothing Then
            Throw New ArgumentNullException("element")
        End If

        element.SetValue(ItemsProperty, value)
    End Sub

    Public Shared ReadOnly ItemsProperty As  _
                           DependencyProperty = DependencyProperty.RegisterAttached("Items", _
                           GetType(IEnumerable(Of EventToCommandItem)), GetType(EventToCommandAggregator), _
                           New UIPropertyMetadata(New EventToCommandItemCollection(), AddressOf ItemsChanged))

    Private Shared Sub ItemsChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
        If d Is Nothing Then Return
        Dim uid As UIElement = TryCast(d, UIElement)
        If uid Is Nothing Then Return
        If e.NewValue Is Nothing Then Return
        If Not data.ContainsKey(uid) Then
            data.Add(uid, e.NewValue)
        End If
        data(uid) = e.NewValue
        For Each elt As EventToCommandItem In data(uid)
            uid.AddHandler(elt.Event, New RoutedEventHandler(AddressOf Handler))
        Next
    End Sub

    Private Shared Sub Handler(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs)
        Dim uid As UIElement = TryCast(sender, UIElement)
        If uid Is Nothing Then Return
        Dim item = (From elt In data(uid)
                   Where e.RoutedEvent Is elt.Event
                   Select elt.Command).FirstOrDefault()
        If item Is Nothing Then Return
        If item.CanExecute(e) Then
            item.Execute(e)
        End If
    End Sub

    Private Shared data As New Dictionary(Of UIElement, ObservableCollection(Of EventToCommandItem))

End Class

Public NotInheritable Class EventToCommandItem
    Inherits DependencyObject



    Public Property [Event] As RoutedEvent
        Get
            Return GetValue(EventProperty)
        End Get

        Set(ByVal value As RoutedEvent)
            SetValue(EventProperty, value)
        End Set
    End Property

    Public Shared ReadOnly EventProperty As DependencyProperty = _
                           DependencyProperty.Register("Event", _
                           GetType(RoutedEvent), GetType(EventToCommandItem), _
                           New UIPropertyMetadata(Nothing))


    Public Property Command As ICommand
        Get
            Return GetValue(CommandProperty)
        End Get

        Set(ByVal value As ICommand)
            SetValue(CommandProperty, value)
        End Set
    End Property

    Public Shared ReadOnly CommandProperty As DependencyProperty = _
                           DependencyProperty.Register("Command", _
                           GetType(ICommand), GetType(EventToCommandItem), _
                           New UIPropertyMetadata(Nothing))






End Class
Public Class EventToCommandItemCollection
    Inherits ObservableCollection(Of EventToCommandItem)

End Class